package ac.svn.accesscontrol.controller.user;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import ac.notification.event.INotificationListener;
import ac.notification.event.NotificationEvent;
import ac.notification.manager.NotificationRegistrar;
import ac.svn.accesscontrol.context.Context;
import ac.svn.accesscontrol.context.IContextConstants;
import ac.svn.accesscontrol.controller.ListingController;
import ac.svn.accesscontrol.manager.SVNInfoManager;
import ac.svn.accesscontrol.store.AccessUnit;
import ac.svn.accesscontrol.store.IConstants;
import ac.svn.accesscontrol.store.ISVNIdentifier;
import ac.svn.accesscontrol.store.ISVNItem;
import ac.svn.accesscontrol.store.SVNGroup;
import ac.svn.accesscontrol.store.SVNLocation;
import ac.svn.accesscontrol.store.SVNUser;
import ac.svn.accesscontrol.ui.IUIConstants;
import ac.svn.accesscontrol.ui.UIComponentLibrary;
import ac.svn.accesscontrol.ui.helper.LocationFilter;
import ac.svn.accesscontrol.ui.panel.UserAccessEditorPanel;
import ac.svn.accesscontrol.ui.panel.UserBasicInfoEditorPanelI;
import ac.svn.accesscontrol.ui.window.UserInformationEditorWindow;
import ac.svn.accesscontrol.utils.UIUtility;
import ac.svn.accesscontrol.utils.Utility;

/***
 * Class manages user's access related information.
 * @author Enigmanic
 *
 */

public class UserLocationAccessController implements INotificationListener, IContextConstants,ISVNIdentifier 
{
	private UserBasicInfoEditorPanelI accessEditorPanel;
	private UserInformationEditorWindow informationEditor;
	private SVNInfoManager infoManager;
	private LocationFilter locFltrAllLoc, locFltrAsndLoc;
	private JTextField txtSearchAsndLoc, txtSearchAllLoc;
	private Context context;
	private JButton addBtn, editButton,deleteButton;


	public UserLocationAccessController(UserInformationEditorWindow editorWindow,UserBasicInfoEditorPanelI userBasicInfoEditorPanelI)
	{
		//Receive target components.
		this.accessEditorPanel = userBasicInfoEditorPanelI;
		this.informationEditor		= editorWindow;

		//Get a reference to SVNInformation manager.
		infoManager = SVNInfoManager.getInfoManager();

		//Get necessary Components from Registry
		locFltrAllLoc = new LocationFilter(UserAccessEditorPanel.ExportedComponents.C_UAR_AVL_LIST_VIEW,UserAccessEditorPanel.ExportedComponents.C_UAR_AVL_LIST_MODEL);
		locFltrAsndLoc = new LocationFilter(UserAccessEditorPanel.ExportedComponents.C_UAR_ASSGND_LIST_VIEW,UserAccessEditorPanel.ExportedComponents.C_UAR_ASSGND_LIST_MODEL);
		ListingController.getListingController().registerListerForNotifications(locFltrAllLoc, IUIConstants.C_LIST_TYPE_LOCATION); 

		txtSearchAllLoc = (JTextField) UIComponentLibrary.getComponent(UserAccessEditorPanel.ExportedComponents.C_UAR_TXT_SEARCH_ALL);
		txtSearchAsndLoc = (JTextField) UIComponentLibrary.getComponent(UserAccessEditorPanel.ExportedComponents.C_UAR_TXT_SEARCH_ASND);

		addBtn 			= this.accessEditorPanel.getAddLocationButton();
		editButton  	= this.accessEditorPanel.getEditLocationButton();
		deleteButton 	= this.accessEditorPanel.getDeleteLocationButton();

		//Unable UI components
		registerListeners();

		//Register for receiving commands and notifications.
		NotificationRegistrar.registerListener(this);
	}

	/***
	 *  Method is responsible for adding listeners on Window's UI Components.
	 */
	private void registerListeners() 
	{
		UIUtility.addPopupListener(accessEditorPanel.getListAvailable(), IConstants.VALUE_POPUP_TYPE_LOCATION);
		/***
		 * Listener handles click on W(Write) check box.
		 */
		accessEditorPanel.getChckbxR().addActionListener(new ActionListener() 
		{
			@Override
			public void actionPerformed(ActionEvent arg0) 
			{
				//Update state of Read/Write in SVNInformationManager
				modifyAccessRights(accessEditorPanel.getChckbxR().isSelected(),accessEditorPanel.getChckbxW().isSelected());						
			}
		});

		/***
		 * Listener handles click on W(Write) check box.
		 */
		accessEditorPanel.getChckbxW().addActionListener(new ActionListener() 
		{
			@Override
			public void actionPerformed(ActionEvent arg0) 
			{
				//Update state of Read/Write in SVNInformationManager
				modifyAccessRights(accessEditorPanel.getChckbxR().isSelected(),accessEditorPanel.getChckbxW().isSelected());
			}
		});

		/***
		 * Listener handles click on delete.
		 */
		accessEditorPanel.getBtnDelete().addActionListener(new ActionListener() 
		{
			@Override
			public void actionPerformed(ActionEvent arg0) 
			{
				removeLocationAccess();
			}
		});

		accessEditorPanel.getListAssigned().addListSelectionListener(new ListSelectionListener() 
		{
			@Override
			public void valueChanged(ListSelectionEvent lse)
			{
				// Process only when value is selected not when previous
				// selection is unselected.
				if (false == lse.getValueIsAdjusting()) 
				{
					// Get selected assigned location.
					ISVNItem selectedAssndLoc = accessEditorPanel.getSelectedAssignedLocation();
					// Check if any selections are made and selection is
					// of type SVNLocation.
					if ((null != selectedAssndLoc)&& (selectedAssndLoc instanceof AccessUnit)) 
					{
						AccessUnit accessUnit = (AccessUnit) selectedAssndLoc;

						// Check whether access permissions are editable
						// for user.
						boolean accessRightsEditable = true;

						for(ISVNItem group : infoManager.getAllGroups() )
						{
							SVNGroup svnGroup = (SVNGroup) group;
							if(svnGroup.getId().equalsIgnoreCase(accessUnit.getOwnerId()))
							{
								accessRightsEditable = false;
								break;
							}
						}
						accessEditorPanel.getChckbxR().setEnabled(	accessRightsEditable);
						accessEditorPanel.getChckbxW().setEnabled(accessRightsEditable);
						accessEditorPanel.getBtnDelete().setEnabled(accessRightsEditable);
						// Show current state of access rights.
						if (null != accessUnit) 
						{
							accessEditorPanel.getChckbxW().setSelected(accessUnit.isWriteEnabled());
							accessEditorPanel.getChckbxR().setSelected(accessUnit.isReadEnabled());
						}
					}
				}
			}
		});

		accessEditorPanel.getBtnHelp().addActionListener(new ActionListener() 
		{

			@Override
			public void actionPerformed(ActionEvent arg0) 
			{
				JOptionPane.showMessageDialog(informationEditor, "<HTML>Following are the rules of modification:<br>" +
						"1. Access rights to location accessible through group cannot be modified.<br>" +
						"2. Access rights to location accessible through absolute user can only be modified.<br><br>" +
						"Modification made here can be reviewed in tag editor component!","Help",JOptionPane.INFORMATION_MESSAGE);				

			}
		});

		accessEditorPanel.getListAvailable().addKeyListener(new KeyAdapter() 
		{
			@Override
			public void keyPressed(KeyEvent e) 
			{
				if(KeyEvent.VK_ENTER == e.getKeyCode())
				{
					//Add association between user and selected location.
					addLocationRights();
				}
			}
		});

		accessEditorPanel.getBtnAddLocation().addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				//Add association between user and selected location.
				addLocationRights();				
			}
		});

		txtSearchAllLoc.addActionListener(new ActionListener() 
		{
			@Override
			public void actionPerformed(ActionEvent arg0)
			{
				String searchText = txtSearchAllLoc.getText();
				locFltrAllLoc.filterList(infoManager.getAllLocations(),searchText);
			}
		});

		txtSearchAllLoc.addKeyListener(new KeyAdapter() 
		{
			@Override
			public void keyReleased(KeyEvent arg0) 
			{
				String searchText = txtSearchAllLoc.getText();
				locFltrAllLoc.filterList(infoManager.getAllLocations(),searchText);
			}
		});

		txtSearchAsndLoc.addKeyListener(new KeyAdapter() 
		{
			@Override
			public void keyReleased(KeyEvent arg0) 
			{
				String strSearchCriteria = txtSearchAsndLoc.getText();
				populateAssignedLocations(strSearchCriteria);
			}
		});

		txtSearchAsndLoc.addFocusListener(new FocusAdapter() 
		{
			@Override
			public void focusGained(FocusEvent e) 
			{
				populateAssignedLocations("");
				super.focusLost(e);
			}
		});

		txtSearchAllLoc.addFocusListener(new FocusAdapter() 
		{

			@Override
			public void focusGained(FocusEvent e) 
			{
				locFltrAllLoc.filterList(infoManager.getAllLocations(),"");
				super.focusLost(e);
			}
		});

		//Button action for quick link to adding location.
		addBtn.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				Utility.sendNotification(CMD_ADD_LOCATION);
			}
		});

		//Button action for quick link to adding location.
		editButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				ISVNItem location = (ISVNItem) accessEditorPanel.getListAvailable().getSelectedValue();
				if(null != location)
				{
					NotificationEvent event = new NotificationEvent(CMD_EDIT_LOCATION);
					event.addParameter(P_LOCATION, location);
					NotificationRegistrar.dispatchNotification(event);
				}
				else
				{
					String message = "Select a location from Available location's List!";
					JOptionPane.showMessageDialog(null, message,"Make Selection",JOptionPane.INFORMATION_MESSAGE);
				}
			}
		});
		//Button action for quick link to adding location.
		deleteButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				ISVNItem location = (ISVNItem) accessEditorPanel.getListAvailable().getSelectedValue();
				if(null != location)
				{
					NotificationEvent event = new NotificationEvent(CMD_DELETE_LOCATION);
					event.addParameter(P_LOCATION, location);
					event.addParameter(P_AUTOMATIC, true);
					NotificationRegistrar.dispatchNotification(event);
				}
				else
				{
					String message = "Select a location from Available location's List!";
					JOptionPane.showMessageDialog(null, message,"Make Selection",JOptionPane.INFORMATION_MESSAGE);
				}

			}
		});
	}

	private boolean locationAlreadyAssigned(SVNLocation AsvnLocation,SVNUser svnUser) 
	{
		if(null == svnUser)
		{
			return false;
		}
		boolean alreadyAssigned = false;
		Set<ISVNItem> allLocations = infoManager.getAllLocations();
		Set<String> assignedLocs = new HashSet<String>();

		for (ISVNItem isvnItem : allLocations) 
		{
			SVNLocation svnLocation = (SVNLocation) isvnItem;
			for (String accessUnitId : svnLocation.getAccessUnits()) 
			{
				AccessUnit accessUnit = infoManager.getAccessUnit(accessUnitId);
				if (svnUser.getId().equalsIgnoreCase(accessUnit.getOwnerId()))
				{
					assignedLocs.add(svnLocation.getId());
					break;
				}
			}
		}

		if (true == assignedLocs.contains(AsvnLocation.getId())) 
		{
			alreadyAssigned = true;
		}

		return alreadyAssigned;
	}


	private void loadUserLocationDetails(SVNUser svnUser) 
	{
		// List all location in available list.
		locFltrAllLoc.filterList(infoManager.getAllLocations(), "");
		//informationEditor.getAccessEditorPanel().getLblUserName().setText("User Name: " + svnUser.getName());
		//informationEditor.getAccessEditorPanel().getLblGroupsg().setText("Group(s): "+ String.valueOf(svnUser.getGroups().size())+ " " + svnUser.getGroups());
		populateAssignedLocations("");
	}

	private void populateAssignedLocations(final String strSearchCriteria) 
	{
		//Check context is given to Controller
		if(null != context)
		{
			//Get user from context.
			SVNUser svnUser = (SVNUser) context.get(KEY_OPERATION_PARAM);

			//Check user is there
			if(null != svnUser)
			{
				Thread listingThread = new Thread(new Runnable() {
					@Override
					public void run() {
						//Get user from context.
						SVNUser svnUser = (SVNUser) context.get(KEY_OPERATION_PARAM);

						//Check user is there
						if(null != svnUser)
						{
							//Get list of all locations
							Set<ISVNItem> allLocations = infoManager.getAllLocations();

							//List for assigned locations.
							List<ISVNItem> assignedLocs = new ArrayList<ISVNItem>();

							for (ISVNItem isvnItem : allLocations) 
							{
								SVNLocation svnLocation = (SVNLocation) isvnItem;
								for (String accessUnitId : svnLocation.getAccessUnits()) 
								{
									AccessUnit accessUnit = infoManager.getAccessUnit(accessUnitId);
									if (svnUser.getId().equalsIgnoreCase(	accessUnit.getOwnerId())	||
											svnUser.getGroups().contains(accessUnit.getOwnerId())) 
									{
										assignedLocs.add(accessUnit);
									}
								}
							}
							//System.err.println("Assigned Locations: " + String.valueOf(assignedLocs.size()));
							locFltrAsndLoc.filterList(assignedLocs, strSearchCriteria);
						}
					}
				});
				listingThread.start();
			}
		}
	}


	/***
	 * Method updates user's access rights to selected location.
	 * @param r
	 * @param w
	 */
	private void modifyAccessRights(boolean r,boolean w)
	{
		//Check context is given to Controller
		if(null != context)	
		{
			SVNUser svnUser =(SVNUser) context.get(KEY_OPERATION_PARAM);

			//Check sufficient information is present in it.
			if(null != svnUser)
			{
				//Get location selected on Assigned list.
				ISVNItem svnLoc = accessEditorPanel.getSelectedAssignedLocation();

				//Check presence and correct type.
				if ((null != svnLoc) && (svnLoc instanceof AccessUnit)) 
				{
					//Get actual access Unit
					AccessUnit unit = (AccessUnit) svnLoc;

					//If read right has been modified.
					//if(true == r)
					{
						unit.setReadEnabled(accessEditorPanel.getChckbxR().isSelected());
					}

					//If write has been modified.
					//if(true == w)
					{
						unit.setWriteEnabled(accessEditorPanel.getChckbxW().isSelected());
					}

					//Update access unit in SVNInformation Manager.
					infoManager.updateAccessUnit(unit);
				}
			}
		}
	}

	/***
	 * Method removes users association with selected location.
	 */
	private void removeLocationAccess()
	{
		//Check context is given to Controller
		if(null != context)
		{
			//Get user from context.
			SVNUser svnUser = (SVNUser) context.get(KEY_OPERATION_PARAM);

			//Check user is there
			if(null != svnUser)
			{
				//Get location selected on Assigned List.
				ISVNItem accessUnit = accessEditorPanel.getSelectedAssignedLocation();

				//Check if at all selection is made and list is populated with SVNLocation objects.
				if ((null != accessUnit) && (accessUnit instanceof AccessUnit)) 
				{
					//Warn user about the consequences.
					JOptionPane.showMessageDialog(informationEditor,accessUnit.getName()+ " would be not be accessible to user.");

					//Remove access unit and its side effects.
					infoManager.removeAccessUnit(accessUnit.getId());

					//Refresh current location assignment details.
					loadUserLocationDetails(svnUser);

				}
			}
		}
	}

	private void addLocationRights() {
		//Check context is given to Controller
		if(null != context)
		{
			//Get user from context.
			SVNUser svnUser = (SVNUser) context.get(KEY_OPERATION_PARAM);

			//Check user is there
			if(null != svnUser)
			{
				//Get selected location from Available locations list.
				ISVNItem item = informationEditor.getBasicInfoEditorPanel().getSelectedAvailableLocation();

				//Check if location is selected.
				if (null != item) 
				{
					//Warn user about default association is of read only type.
					JOptionPane.showMessageDialog(informationEditor,item.getName() + " would be added in read mode.");

					//Get actual location.
					SVNLocation location = (SVNLocation) item;

					//Check if it is assigned already!
					if (false == locationAlreadyAssigned(location,svnUser)) 
					{
						//Create a new access unit for location and current user.
						AccessUnit accessUnit = new AccessUnit(svnUser.getId(),true, false);

						//Update information.
						accessUnit.setLocationPath(location.getId());
						location.addAccessUnit(accessUnit.getId());

						//Update access unit in SVNInformation Manager.
						infoManager.updateAccessUnit(accessUnit);

						//Update location in SVNInformation Manager.
						infoManager.updateLocation(location);

						//Relist user's current access rights.
						loadUserLocationDetails(svnUser);
					}
				}
			}
		}
	}

	/**
	 * @return the context
	 */
	public Context getContext() {
		return context;
	}

	/**
	 * @param context the context to set
	 */
	public void setContext(Context context) 
	{
		this.context = context;
	}

	//=========================================================//
	// 													OVERRIDDEN SECTION																//
	//=========================================================//

	@Override
	public Set<String> declaredSubscriptionSet() {
		Set<String> subscriptionSet = new HashSet<String>();
		subscriptionSet.add(CMD_SHOW_USER_ACCESS_RIGHT);
		subscriptionSet.add(N_UIM_GROUP_MEM_UPDATE);
		subscriptionSet.add(N_LOCATION_ADDED);
		subscriptionSet.add(N_LOCATION_MODIFIED);
		subscriptionSet.add(N_LOCATION_REMOVED);
		return subscriptionSet;
	}

	
	@Override
	public void receiveEvent(NotificationEvent notificationEvent) 
	{
		loadAccessDetials();
	}

	private void loadAccessDetials()
	{
		if(null != context)
		{
			SVNUser svnUser = (SVNUser) context.get(KEY_OPERATION_PARAM);
			loadUserLocationDetails(svnUser );
		}
	}



}
